/*************************************************************************
 * File Name   : drv_spi.c
 * Brief       :
 * Change Logs :
 * Date           Author       Notes
 * 2022\03\02     liqi         The first version.
*************************************************************************/

#include <rtdevice.h>
#include <rthw.h>
#include <board.h>
#include "drv_spi.h"
#include <string.h>

// #define DRV_DEBUG
#define LOG_TAG              "drv.spi"
#include <drv_log.h>

#define SPI_MASTER          SPI1
#define SPI_MASTER_CLK      RCC_APB2_PERIPH_SPI1
#define SPI_MASTER_GPIO     GPIOA
#define SPI_MASTER_GPIO_CLK RCC_APB2_PERIPH_GPIOA
#define SPI_MASTER_PIN_NSS  GPIO_PIN_4
#define SPI_MASTER_PIN_SCK  GPIO_PIN_5
#define SPI_MASTER_PIN_MISO GPIO_PIN_6
#define SPI_MASTER_PIN_MOSI GPIO_PIN_7

/* SPI SPE mask */
#define CTRL1_SPIEN_ENABLE  ((uint16_t)0x0040)
#define CTRL1_SPIEN_DISABLE ((uint16_t)0xFFBF)

struct n32_hw_spi_cs
{
    GPIO_Module *GPIOx;
    uint16_t GPIO_Pin;
};

struct n32_spi
{
    SPI_Module *instance;
    char *bus_name;
    SPI_InitType init;
    struct rt_spi_bus spi_bus;
};


struct n32_spi spi1 = {0};

static int spi_receive(SPI_InitType spi_init, SPI_Module *spi_instance, uint8_t *pData, uint16_t Size, uint32_t Timeout);
static int spi_transmitreceive(SPI_InitType spi_init, SPI_Module *spi_instance, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout);
static int spi_transmit(SPI_InitType spi_init, SPI_Module *spi_instance, uint8_t *pData, uint16_t Size, uint32_t Timeout);
static rt_err_t spi_configure(struct rt_spi_device *device, struct rt_spi_configuration *cfg);
static rt_uint32_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message);
// static void RCC_Configuration(void);
// static void GPIO_Configuration(void);

static const struct rt_spi_ops n32_spi_ops =
{
    spi_configure,
    spi_xfer,
};


static int n32_hw_spi_bus_init(void)
{
    struct n32_spi *spi_bus;

    spi_bus = &spi1;
    spi_bus->instance = SPI1;
    spi_bus->bus_name = "spi1";
    spi_bus->spi_bus.parent.user_data = &spi1;

    SPI_EnableCalculateCrc(spi_bus->instance, DISABLE);
    n32_msp_spi_init(spi_bus->instance);

    rt_spi_bus_register(&spi_bus->spi_bus, spi_bus->bus_name, &n32_spi_ops);
    LOG_D("%s bus init done", spi_bus->bus_name);

    return 0;
}
INIT_BOARD_EXPORT(n32_hw_spi_bus_init);

// 0xx:HCLK 不分频
// 100:HCLK 2 分频
// 101:HCLK 4 分频
// 110:HCLK 8 分频
// 111:HCLK 16 分频
uint8_t APBPrescTable[8] = {1, 1, 1, 1, 2, 4, 8, 16};


static rt_err_t spi_configure(struct rt_spi_device *device,
                              struct rt_spi_configuration *cfg)
{
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);

    struct n32_spi *spi_drv =  rt_container_of(device->bus, struct n32_spi, spi_bus);


    SPI_InitType *spi_init = &spi_drv->init;

    if (cfg->mode & RT_SPI_SLAVE)
    {
        spi_init->SpiMode = SPI_MODE_SLAVE;
    }
    else
    {
        LOG_D("master");
        spi_init->SpiMode = SPI_MODE_MASTER;
    }

    if (cfg->mode & RT_SPI_3WIRE)
    {
        spi_init->DataDirection = SPI_DIR_SINGLELINE_RX;
    }
    else
    {
        LOG_D("fullduplex");
        spi_init->DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;
    }

    if (cfg->data_width == 8)
    {
        LOG_D("8 bits");
        spi_init->DataLen = SPI_DATA_SIZE_8BITS;
    }
    else if (cfg->data_width == 16)
    {
        spi_init->DataLen = SPI_DATA_SIZE_16BITS;
    }
    else
    {
        return RT_EIO;
    }

    if (cfg->mode & RT_SPI_CPHA)
    {
        spi_init->CLKPHA = SPI_CLKPHA_SECOND_EDGE;
        LOG_D("pha 2 edge");
    }
    else
    {
        spi_init->CLKPHA = SPI_CLKPHA_FIRST_EDGE;
        LOG_D("pha 1 edge");
    }

    if (cfg->mode & RT_SPI_CPOL)
    {
        spi_init->CLKPOL = SPI_CLKPOL_HIGH;
        LOG_D("pol high");
    }
    else
    {
        spi_init->CLKPOL = SPI_CLKPOL_LOW;
        LOG_D("pol low");
    }

    if (cfg->mode & RT_SPI_NO_CS)
    {
        spi_init->NSS = SPI_NSS_SOFT;
    }
    else
    {
        spi_init->NSS = SPI_NSS_SOFT;
    }

    RCC_ClocksType RCC_ClocksStatus;
    RCC_GetClocksFreqValue(&RCC_ClocksStatus);

    uint32_t SPI_APB_CLOCK;
    // SPI_APB_CLOCK = SystemCoreClock >> APBPrescTable[(RCC->CFG & RCC_CFG_APB2PRES) >> 10U];
    SPI_APB_CLOCK = RCC_ClocksStatus.Pclk2Freq;


    if (cfg->max_hz >= SPI_APB_CLOCK / 2)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_2;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 4)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_4;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 8)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_8;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 16)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_16;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 32)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_32;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 64)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_64;
    }
    else if (cfg->max_hz >= SPI_APB_CLOCK / 128)
    {
        spi_init->BaudRatePres = SPI_BR_PRESCALER_128;
    }
    else
    {
        /*  min prescaler 256 */
        spi_init->BaudRatePres = SPI_BR_PRESCALER_256;
    }

    LOG_D("sys freq: %d, pclk2 freq: %d, SPI limiting freq: %d, BaudRatePres: %d",
          RCC_ClocksStatus.SysclkFreq,
          RCC_ClocksStatus.Pclk2Freq,
          cfg->max_hz,
          spi_init->BaudRatePres);

    if (cfg->mode & RT_SPI_MSB)
    {
        spi_init->FirstBit = SPI_FB_MSB;
    }
    else
    {
        spi_init->FirstBit = SPI_FB_LSB;
    }

    spi_init->CRCPoly = 7;

    SPI_Init(spi_drv->instance, spi_init);

    SPI_Enable(spi_drv->instance, ENABLE);

    LOG_D("%s init done", spi_drv->bus_name);
    return RT_EOK;
}

static rt_uint32_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    int state;
    rt_size_t message_length, already_send_length;
    rt_uint16_t send_length;
    rt_uint8_t *recv_buf;
    const rt_uint8_t *send_buf;

    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);
    RT_ASSERT(device->bus->parent.user_data != RT_NULL);
    RT_ASSERT(message != RT_NULL);

    struct n32_spi *spi_drv =  rt_container_of(device->bus, struct n32_spi, spi_bus);
    SPI_Module *spi_instance = spi_drv->instance;
    SPI_InitType *spi_init = &spi_drv->init;
    struct n32_hw_spi_cs *cs = device->parent.user_data;

    // SPI_Enable(spi_instance, ENABLE);
    if (message->cs_take)
    {
        // LOG_I("take");
        GPIO_WriteBit(cs->GPIOx, cs->GPIO_Pin, Bit_RESET);
    }

    LOG_D("%s transfer prepare and start", spi_drv->bus_name);
    LOG_D("%s sendbuf: %X, recvbuf: %X, length: %d",
          spi_drv->bus_name,
          (uint32_t)message->send_buf,
          (uint32_t)message->recv_buf, message->length);

    message_length = message->length;
    recv_buf = message->recv_buf;
    send_buf = message->send_buf;
    while (message_length)
    {
        if (message_length > 65535)
        {
            send_length = 65535;
            message_length = message_length - 65535;
        }
        else
        {
            send_length = message_length;
            message_length = 0;
        }

        /* calculate the start address */
        already_send_length = message->length - send_length - message_length;
        send_buf = (rt_uint8_t *)message->send_buf + already_send_length;
        recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length;

        if (message->send_buf && message->recv_buf)
        {
            LOG_D("trans and rece");
            state = spi_transmitreceive(*spi_init, spi_instance, (uint8_t *)send_buf, (uint8_t *)recv_buf, send_length, 1000);
        }
        else if (message->send_buf)
        {
            LOG_D("trans");
            state = spi_transmit(*spi_init, spi_instance, (uint8_t *)send_buf, send_length, 1000);
        }
        else
        {
            LOG_D("recv");
            rt_memset((uint8_t *)recv_buf, 0xff, send_length);
            state = spi_receive(*spi_init, spi_instance, (uint8_t *)recv_buf, send_length, 1000);
        }

        if (state != 0)
        {
            LOG_I("spi transfer error : %d", state);
            message->length = 0;
        }
        else
        {
            LOG_D("%s transfer done", spi_drv->bus_name);
        }
    }

    if (message->cs_release)
    {
        // LOG_I("release");
        GPIO_WriteBit(cs->GPIOx, cs->GPIO_Pin, Bit_SET);
    }
    // SPI_Enable(spi_instance, DISABLE);

    return message->length;
}


int spi_wait_until_timeout(SPI_InitType spi_init, SPI_Module *spi_instance, uint16_t spi_i2s_flag, FlagStatus flag, rt_tick_t Timeout, rt_tick_t tickstart)
{
    /* Wait until TXE flag is set to send data */
    while (SPI_I2S_GetStatus(spi_instance, spi_i2s_flag) != flag)
    {
        // LOG_I("status: %d", SPI_I2S_GetStatus(spi_instance, SPI_I2S_TE_FLAG));
        // LOG_I("tick: start->%x cur->%x timeout->%x", tickstart, rt_tick_get() * 1000 / RT_TICK_PER_SECOND, Timeout);
        if ((((rt_tick_get() * 1000 / RT_TICK_PER_SECOND - tickstart) >=  Timeout) && ((Timeout != 0xFFFFFFFFU))) || (Timeout == 0U))
        {
            return RT_ETIMEOUT;
        }
    }
    return RT_EOK;
}



static int spi_transmit(SPI_InitType spi_init, SPI_Module *spi_instance, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
    uint32_t tickstart = 0U;
    int errorcode = 0;
    __IO uint16_t              TxXferCount;
    __IO uint32_t SPITimeout;

    /* Init tickstart for timeout management*/
    tickstart = rt_tick_get() * 1000 / RT_TICK_PER_SECOND;

    if ((pData == NULL) || (Size == 0))
    {
        errorcode = 1;
        goto error;
    }

    TxXferCount = Size;

    /* Configure communication direction : 1Line */
    if (spi_init.DataDirection == SPI_DIR_SINGLELINE_RX)
    {
        LOG_I("singleline_rx");
        // spi_instance->CTRL1 |= SPI_CR1_BIDIOE;
        spi_instance->CTRL1 |= SPI_BIDIRECTION_TX;
    }

    /* Check if the SPI is already enabled */
    // if ((spi_instance->CTRL1 & CTRL1_SPIEN_ENABLE) != CTRL1_SPIEN_ENABLE)
    // {
    //     /* Enable SPI peripheral */
    //     spi_instance->CTRL1 |=  CTRL1_SPIEN_ENABLE;
    // }

    /* Transmit data in 16 Bit mode */
    if (spi_init.DataLen == SPI_DATA_SIZE_16BITS)
    {
        if ((spi_init.SpiMode == SPI_MODE_SLAVE) || (TxXferCount == 0x01))
        {
            spi_instance->DAT = *((uint16_t *)pData);

            pData += sizeof(uint16_t);
            TxXferCount--;
        }
        /* Transmit data in 16 Bit mode */
        while (TxXferCount > 0U)
        {
            SPITimeout = 0x1000;
            /* Wait until TXE flag is set to send data */
            while (SPI_I2S_GetStatus(spi_instance, SPI_I2S_TE_FLAG) == RESET)
            {
                if ((SPITimeout--) == 0)
                {
                    LOG_E("%s: %d spi timeout", __FUNCTION__, __LINE__);
                    errorcode = 3;
                    goto error;
                }
            }
            /* 写入数据寄存器，把要写入的数据写入发送缓冲区 */
            spi_instance->DAT = *((uint16_t *)pData);

            pData += sizeof(uint16_t);
            TxXferCount--;
        }
    }
    /* Transmit data in 8 Bit mode */
    else
    {
        if ((spi_init.SpiMode == SPI_MODE_SLAVE) || (TxXferCount == 0x01U))
        {
            // *((__IO uint8_t *)&spi_instance->DAT) = (*pData);
            SPI_I2S_TransmitData(spi_instance, *pData);

            pData += sizeof(uint8_t);
            TxXferCount--;
        }

        while (TxXferCount > 0)
        {
            SPITimeout = 0x1000;

#if 0
            /* 等待发送缓冲区为空，TXE事件 */
            if (spi_wait_until_timeout(spi_init, spi_instance, SPI_I2S_TE_FLAG, SET, SPITimeout, tickstart) != 0)
            {
                LOG_E("%s:%d spi timeout", __FUNCTION__, __LINE__);
                errorcode = 3;
                goto error;
            }
#else
            while (SPI_I2S_GetStatus(spi_instance, SPI_I2S_TE_FLAG) == RESET)
            {
                if ((SPITimeout--) == 0)
                {
                    LOG_E("%s:%d spi timeout", __FUNCTION__, __LINE__);
                    errorcode = 3;
                    goto error;
                }
            }
#endif
            /* 写入数据寄存器，把要写入的数据写入发送缓冲区 */
            // *((__IO uint8_t *)&spi_instance->DAT) = (*pData);
            SPI_I2S_TransmitData(spi_instance, *pData);
            pData += sizeof(uint8_t);
            TxXferCount--;

        }
    }

    /* Wait until TXE flag */
    if (spi_wait_until_timeout(spi_init, spi_instance, SPI_I2S_TE_FLAG, SET, Timeout, tickstart) != 0)
    {
        errorcode = 3;
        LOG_W("%s spi wait txe timeout", __FUNCTION__);
        goto error;
    }

    /* Check Busy flag */
    if (spi_wait_until_timeout(spi_init, spi_instance, SPI_I2S_BUSY_FLAG, RESET, Timeout, tickstart) != 0)
    {
        errorcode = 1;
        LOG_W("%s spi wait busy timeout", __FUNCTION__);
        goto error;
    }

error:
    return errorcode;
}

static int spi_transmitreceive(SPI_InitType spi_init, SPI_Module *spi_instance, uint8_t *pTxData, uint8_t *pRxData, uint16_t Size, uint32_t Timeout)
{
    uint32_t tmp = 0U;

    uint32_t tickstart = 0U;
    /* Variable used to alternate Rx and Tx during transfer */
    uint32_t txallowed = 1U;
    int errorcode = 0;
    __IO uint16_t              TxXferCount;
    __IO uint16_t              RxXferCount;

    /* Init tickstart for timeout management*/
    tickstart = rt_tick_get() * 1000 / RT_TICK_PER_SECOND;

    tmp  = spi_init.SpiMode;

    if (!((tmp == SPI_MODE_MASTER) && (spi_init.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX)))
    {
        errorcode = 2;
        goto error;
    }

    if ((pTxData == NULL) || (pRxData == NULL) || (Size == 0))
    {
        errorcode = 1;
        LOG_E("mem is none");
        goto error;
    }

    TxXferCount = Size;
    RxXferCount = Size;

    /* Check if the SPI is already enabled */
    // if ((spi_instance->CTRL1 & CTRL1_SPIEN_ENABLE) != CTRL1_SPIEN_ENABLE)
    // {
    //     /* Enable SPI peripheral */
    //     spi_instance->CTRL1 |=  CTRL1_SPIEN_ENABLE;
    // }

    /* Transmit and Receive data in 16 Bit mode */
    if (spi_init.DataLen == SPI_DATA_SIZE_16BITS)
    {
        if ((spi_init.SpiMode == SPI_MODE_SLAVE) || (TxXferCount == 0x01U))
        {
            SPI_I2S_ReceiveData(spi_instance);
            spi_instance->DAT = *((uint16_t *)pTxData);

            pTxData += sizeof(uint16_t);
            TxXferCount--;
        }
        while ((TxXferCount > 0U) || (RxXferCount > 0U))
        {
            /* Check TXE flag */
            if (txallowed && (TxXferCount > 0U) && (SPI_I2S_GetStatus(spi_instance, SPI_I2S_TE_FLAG) == SET))
            {
                SPI_I2S_ReceiveData(spi_instance);
                spi_instance->DAT = *((uint16_t *)pTxData);
                pTxData += sizeof(uint16_t);
                TxXferCount--;
                /* Next Data is a reception (Rx). Tx not allowed */
                txallowed = 0U;
            }

            /* Check RXNE flag */
            if ((RxXferCount > 0U) && (SPI_I2S_GetStatus(spi_instance, SPI_I2S_RNE_FLAG) == SET))
            {
                *((uint16_t *)pRxData) = spi_instance->DAT;
                pRxData += sizeof(uint16_t);
                RxXferCount--;
                /* Next Data is a Transmission (Tx). Tx is allowed */
                txallowed = 1U;
            }
            if ((Timeout != 0xFFFFFFFFU) && ((rt_tick_get() * 1000 / RT_TICK_PER_SECOND - tickstart) >=  Timeout))
            {
                errorcode = 3;
                goto error;
            }
        }
    }
    /* Transmit and Receive data in 8 Bit mode */
    else
    {
        if ((spi_init.SpiMode == SPI_MODE_SLAVE) || (TxXferCount == 0x01U))
        {
            // must clear data register, or cause read data error
            if (SPI_I2S_GetStatus(spi_instance, SPI_I2S_RNE_FLAG) == SET)
            {
                LOG_D("read one byte not empty, why");
                *(uint8_t *)pRxData = SPI_I2S_ReceiveData(spi_instance);
            }
            SPI_I2S_TransmitData(spi_instance, (*pTxData));

            LOG_D("read one byte");
        }
        // TODO: data may loss
        else
        {
            // must clear data register, or cause read data error
            if (SPI_I2S_GetStatus(spi_instance, SPI_I2S_RNE_FLAG) == SET)
            {
                LOG_D("rx not empty, why");
                *(uint8_t *)pRxData = SPI_I2S_ReceiveData(spi_instance);
            }
            // SPI_I2S_TransmitData(spi_instance, (*pTxData));
            // while (SPI_I2S_GetStatus(spi_instance, SPI_I2S_TE_FLAG) == RESET);
        }


        while ((TxXferCount > 0U) || (RxXferCount > 0U))
        {

            if (txallowed && (TxXferCount > 0U) && (SPI_I2S_GetStatus(spi_instance, SPI_I2S_TE_FLAG) == SET))
            {
                SPI_I2S_TransmitData(spi_instance, (*pTxData));
                // LOG_D("send %X", *(uint8_t *)pTxData);
                pTxData += sizeof(uint8_t);
                TxXferCount--;
                txallowed = 0U;
            }

            if ((RxXferCount > 0U) && SPI_I2S_GetStatus(spi_instance, SPI_I2S_RNE_FLAG) == SET)
            {
                *(uint8_t *)pRxData = SPI_I2S_ReceiveData(spi_instance);

                pRxData += sizeof(uint8_t);
                RxXferCount--;
                txallowed = 1U;
            }

            if ((Timeout != 0xFFFFFFFFU) && ((rt_tick_get() * 1000 / RT_TICK_PER_SECOND - tickstart) >=  Timeout))
            {
                errorcode = 3;
                goto error;
            }
        }
        LOG_HEX("recv", 16, pRxData - Size * sizeof(uint8_t), Size);
    }

    /* Wait until TXE flag */
    if (spi_wait_until_timeout(spi_init, spi_instance, SPI_I2S_TE_FLAG, SET, Timeout, tickstart) != 0)
    {
        errorcode = 3;
        LOG_W("%s spi wait txe timeout", __FUNCTION__);
        goto error;
    }

    /* Check Busy flag */
    if (spi_wait_until_timeout(spi_init, spi_instance, SPI_I2S_BUSY_FLAG, RESET, Timeout, tickstart) != 0)
    {
        errorcode = 1;
        LOG_W("%s spi wait busy timeout", __FUNCTION__);
        LOG_W("spi busy status %x", SPI_I2S_GetStatus(spi_instance, SPI_I2S_BUSY_FLAG));
        goto error;
    }

error :
    return errorcode;
}

int get_busy_status(void)
{
    struct n32_spi *spi_drv =  &spi1;
    SPI_Module *spi_instance = spi_drv->instance;
    LOG_W("spi busy status %x", SPI_I2S_GetStatus(spi_instance, SPI_I2S_BUSY_FLAG));
		return RT_EOK;
}
MSH_CMD_EXPORT(get_busy_status, test);

static int spi_receive(SPI_InitType spi_init, SPI_Module *spi_instance, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
    uint32_t tickstart = 0U;
    int errorcode = 0;
    __IO uint16_t              RxXferCount;
    __IO uint32_t             SPITimeout;

    if ((spi_init.SpiMode == SPI_MODE_MASTER) && (spi_init.DataDirection == SPI_DIR_DOUBLELINE_FULLDUPLEX))
    {
        /* Call transmit-receive function to send Dummy data on Tx line and generate clock on CLK line */
        LOG_D("jump to trans and rece");
        return spi_transmitreceive(spi_init, spi_instance, pData, pData, Size, Timeout);
    }

    /* Init tickstart for timeout management*/
    tickstart = rt_tick_get() * 1000 / RT_TICK_PER_SECOND;

    if ((pData == NULL) || (Size == 0))
    {
        errorcode = 1;
        goto error;
    }

    RxXferCount = Size;

    /* Configure communication direction: 1Line */
    if (spi_init.DataDirection == SPI_DIR_SINGLELINE_RX)
    {
        // spi_instance->CTRL1 &= (~SPI_CR1_BIDIOE);
        spi_instance->CTRL1 &= SPI_BIDIRECTION_RX;
    }

    /* Check if the SPI is already enabled */
    if ((spi_instance->CTRL1 & CTRL1_SPIEN_ENABLE) != CTRL1_SPIEN_ENABLE)
    {
        /* Enable SPI peripheral */
        spi_instance->CTRL1 |=  CTRL1_SPIEN_ENABLE;
    }

    /* Receive data in 8 Bit mode */
    if (spi_init.DataLen == SPI_DATA_SIZE_8BITS)
    {
        /* Transfer loop */
        while (RxXferCount > 0U)
        {
            SPITimeout = 0x1000;

            /* 等待发送缓冲区为空，TXE事件 */
            while (SPI_I2S_GetStatus(spi_instance, SPI_I2S_RNE_FLAG) == RESET)
            {
                if ((SPITimeout--) == 0)
                {
                    LOG_E("%s:%d spi timeout", __FUNCTION__, __LINE__);
                    errorcode = 3;
                    goto error;
                }
            }

            *(uint8_t *)pData = spi_instance->DAT;

            pData += sizeof(uint8_t);
            RxXferCount--;
        }
    }
    else
    {
        /* Transfer loop */
        while (RxXferCount > 0U)
        {
            SPITimeout = 0x1000;
            /* Check the RXNE flag */
            while (SPI_I2S_GetStatus(spi_instance, SPI_I2S_RNE_FLAG) == RESET)
            {
                if ((SPITimeout--) == 0)
                {
                    LOG_E("%s:%d spi timeout", __FUNCTION__, __LINE__);
                    errorcode = 3;
                    goto error;
                }
            }
            *((uint16_t *)pData) = spi_instance->DAT;
            pData += sizeof(uint16_t);
            RxXferCount--;
        }
    }

    /* Check the end of the transaction */
    if ((spi_init.SpiMode == SPI_MODE_MASTER) && ((spi_init.DataDirection == SPI_DIR_SINGLELINE_RX) || (spi_init.DataDirection == SPI_DIR_DOUBLELINE_RONLY)))
    {
        /* Disable SPI peripheral */
        spi_instance->CTRL1 &= (~CTRL1_SPIEN_ENABLE);
    }

error :
    return errorcode;
}

rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_Module *cs_gpiox, uint16_t cs_gpio_pin)
{
    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);

    rt_err_t result;
    struct rt_spi_device *spi_device;
    struct n32_hw_spi_cs *cs_pin;

    /* initialize the cs pin && select the slave*/
    GPIO_InitType GPIO_InitStructure;
    GPIO_InitStructure.Pin = cs_gpio_pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitPeripheral(cs_gpiox, &GPIO_InitStructure);
    GPIO_WriteBit(cs_gpiox, cs_gpio_pin, Bit_SET);

    /* attach the device to spi bus*/
    spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
    RT_ASSERT(spi_device != RT_NULL);
    cs_pin = (struct n32_hw_spi_cs *)rt_malloc(sizeof(struct n32_hw_spi_cs));
    RT_ASSERT(cs_pin != RT_NULL);
    cs_pin->GPIOx = cs_gpiox;
    cs_pin->GPIO_Pin = cs_gpio_pin;
    result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, (void *)cs_pin);

    if (result != RT_EOK)
    {
        LOG_E("%s attach to %s faild, %d\n", device_name, bus_name, result);
    }

    RT_ASSERT(result == RT_EOK);

    LOG_D("%s attach to %s done", device_name, bus_name);

    return result;
}
